﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Bettzueche.RLGlue.Generics;

namespace Bettzueche.RLGlue.Codec.Network {
    /// <summary>
    /// Abstract Environment middleware.
    /// </summary>
    /// <seealso cref="Bettzueche.RLGlue.Codec.Network.ClientEnvironment"/>
    /// /// <seealso cref="Bettzueche.RLGlue.Codec.Network.GenericClientEnvironment"/>
    public abstract class AbstractClientEnvironment : IClientEnvironment{

        /// <summary>predefined String "Unknown Message: ".</summary>
        protected const String kUnknownMessage = "ClientEnvironment :: Unknown Message: ";
        /// <summary>Network compononent for serialized communication with RLGlue.exe</summary>
        protected Network network;
        /// <summary>Reference to Environment Impl.</summary>
        protected AnEnvironment environment;
        protected volatile bool killedFromAbove = false;

        #region IClientEnvironment Members

        /// <summary>
        /// Called by EnvironmentLoader, connect to glue.exe
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <param name="timeout"></param>
        public virtual void Connect(String host, int port, int timeout) {
            network.Connect(host, port, timeout);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kEnvironmentConnection);
            network.PutInt(0); // No body to this packet
            network.FlipSendBuffer();
            network.Send();
        }

        /// <summary>
        /// Closes the connection.
        /// </summary>
        public virtual void Close() {
            network.Close();
        }

        /// <summary>
        /// Runs the environment event loop.
        /// </summary>
        public virtual void RunEnvironmentEventLoop() {
            int envState = 0;
            int dataSize = 0;
            int recvSize = 0;
            int remaining = 0;

            do {
                try {
                    network.ClearRecvBuffer();
                    recvSize = network.Recv(8) - 8; // We may have received the header and part of the payload
                    // We need to keep track of how much of the payload was recv'd

                    envState = network.GetInt(0);
                    dataSize = network.GetInt(Protocol.kIntSize);

                    remaining = dataSize - recvSize;
                    if (remaining < 0) {
                        remaining = 0;
                    }

                    network.Recv(remaining);
                }
                catch (RLGlueException e) {
                    System.Diagnostics.Trace.Fail("Failed to start Environment.", e.Message);
                    Environment.FailFast("Failed to start Environment.", e); ;
                }

                network.FlipRecvBuffer();

                // We have already received the header, now we need to discard it.
                network.GetInt();
                network.GetInt();

                switch (envState) {
                    case Protocol.kEnvInit:
                        onEnvInit();
                        break;

                    case Protocol.kEnvStart:
                        onEnvStart();
                        break;

                    case Protocol.kEnvStep:
                        onEnvStep();
                        break;

                    case Protocol.kEnvCleanup:
                        onEnvCleanup();
                        break;

                    case Protocol.kEnvMessage:
                        onEnvMessage();
                        break;

                    case Protocol.kRLTerm:
                        break;

                    default:
                        System.Diagnostics.Trace.Fail(kUnknownMessage + envState, "Error while running the Environment. " + kUnknownMessage + envState);
                        //Environment.FailFast("Error while running the Environment. " + kUnknownMessage + envState);
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Error while running the Environment. " + kUnknownMessage + envState);
                        Console.Write("Press any Key to quit.");
                        Console.ReadKey(false);
                        Environment.Exit(1);
                        break;
                }

                network.FlipSendBuffer();
                network.Send();

            } while (envState != Protocol.kRLTerm && !killedFromAbove);
        }

        
        /// <summary>
        /// Kills the Environment process in the experiment.
        /// </summary>
        public virtual void KillProcess() {
            killedFromAbove = true;
        }

        #endregion

        #region RMI Handlers

        /// <summary>
        /// Handles a EnvMessage RMI.
        /// </summary>
        protected abstract void onEnvMessage();

        /// <summary>
        /// Handles a EnvCleanup RMI.
        /// </summary>
        protected abstract void onEnvCleanup();

        /// <summary>
        /// Handles a EnvStep RMI.
        /// </summary>
        protected abstract void onEnvStep();

        /// <summary>
        /// Handles a EnvStart RMI.
        /// </summary>
        protected abstract void onEnvStart();

        /// <summary>
        /// Handles a EnvInit RMI.
        /// </summary>
        protected abstract void onEnvInit();

        #endregion
    }
}
